package com.relytosoft.mizar.media.analyze.services.service.system.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.relytosoft.dubhe.common.api.entity.system.SysFile;
import com.relytosoft.dubhe.common.api.service.RemoteFileService;
import com.relytosoft.dubhe.common.core.base.page.PageQuery;
import com.relytosoft.dubhe.common.core.object.ResponseResult;
import com.relytosoft.dubhe.common.core.utils.BeanCopyUtils;
import com.relytosoft.mizar.media.analyze.api.entity.system.AyGroupBase;
import com.relytosoft.mizar.media.analyze.api.entity.system.AyPersonFace;
import com.relytosoft.mizar.media.analyze.api.entity.system.AyPersonGroup;
import com.relytosoft.mizar.media.analyze.api.entity.system.AyPersonInfo;
import com.relytosoft.mizar.media.analyze.api.pojo.vo.AyPersonInfoVo;
import com.relytosoft.mizar.media.analyze.api.pojo.vo.SysDictVo;
import com.relytosoft.mizar.media.analyze.services.mapper.system.AyPersonInfoMapper;
import com.relytosoft.mizar.media.analyze.services.service.system.AyGroupBaseService;
import com.relytosoft.mizar.media.analyze.services.service.system.AyPersonFaceService;
import com.relytosoft.mizar.media.analyze.services.service.system.AyPersonGroupService;
import com.relytosoft.mizar.media.analyze.services.service.system.AyPersonInfoService;
import com.relytosoft.mizar.media.analyze.services.utils.Base64Utils;
import com.relytosoft.mizar.media.common.constant.MediaMqConstants;
import com.relytosoft.mizar.media.common.emqx.client.MqttOp;
import com.relytosoft.mizar.media.common.entity.pojo.dto.FacePersonReqDto;
import com.relytosoft.mizar.media.common.entity.pojo.dto.FeatureExtractionReqDto;
import com.relytosoft.mizar.media.external.api.analyze.service.FaceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.springframework.util.FileCopyUtils.BUFFER_SIZE;

/**
 * @author lj
 * @date 2022/5/12 11:27
 */
@Service
public class AyPersonInfoServiceImpl extends ServiceImpl<AyPersonInfoMapper, AyPersonInfo> implements AyPersonInfoService {

    private static final Logger log = LoggerFactory.getLogger(AyPersonInfoServiceImpl.class);

    @Resource
    private AyPersonGroupService ayPersonGroupService;

    @Resource
    private AyGroupBaseService ayGroupBaseService;

    @Resource
    private AyPersonFaceService ayPersonFaceService;

    @Resource
    private RemoteFileService remoteFileService;

    @Resource
    private FaceService faceService;

    @Override
    public IPage<AyPersonInfo> selectAyPersonInfoPageList(PageQuery pageQuery, AyPersonInfo ayPersonInfo) {
        if (StrUtil.isBlank(pageQuery.getOrderByColumn())) {
            pageQuery.setOrderByColumn("id");
            pageQuery.setIsAsc("desc");
        }
        Map<Integer, String> idCard = selectByValue("idCard");
        Map<Integer, String> education = selectByValue("education");
        Page<AyPersonInfo> page = baseMapper.selectPageInfoList(pageQuery.build(), ayPersonInfo);
        List<AyPersonInfo> records = page.getRecords();
        records.forEach(item -> {
            item.setIdTypeString(idCard.get(item.getIdType()));
            item.setEducationString(education.get(item.getEducation()));
        });
        return page;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> addAyPersonInfo(AyPersonInfo ayPersonInfo) {
        HashMap<String, Object> map = new HashMap<>(36);
        ayPersonInfo.setStatus(1);
        //1. 人像信息保
        boolean save = this.save(ayPersonInfo);
        List<AyGroupBase> ayGroupBaseList = ayGroupBaseService.list(new QueryWrapper<AyGroupBase>().lambda().in(AyGroupBase::getId, ayPersonInfo.getGroupIds()));
        List<String> plateformGroupIds = ayGroupBaseList.stream().map(AyGroupBase::getPlateformGroupId).collect(Collectors.toList());
        String base64Image = Base64Utils.picToBase64(ayPersonInfo.getImageUrl());
        if (save) {
            //2. 封装库和人关系，批量保存
            save = savaAyPersonGroupList(ayPersonInfo, ayGroupBaseList);
        }
        if (save) {
            //3. 人像关联图片和特征值处理
            save = savaAyPersonFaceInfo(ayPersonInfo, base64Image);
        }
        if (save) {
            map.put("base64Image", base64Image);
            map.put("plateformGroupIds", plateformGroupIds);
        } else {
            return new HashMap<>(12);
        }
        return map;
    }

    private boolean savaAyPersonFaceInfo(AyPersonInfo ayPersonInfo, String base64Image) {
        AyPersonFace ayPersonFace = new AyPersonFace();
        ayPersonFace.setPersonId(ayPersonInfo.getId());
        ayPersonFace.setImageId(ayPersonInfo.getImageId());
        ayPersonFace.setImageUrl(ayPersonInfo.getImageUrl());
        ayPersonFace.setFeature(remoteGetFeature(base64Image));
        return ayPersonFaceService.save(ayPersonFace);
    }

    private boolean savaAyPersonGroupList(AyPersonInfo ayPersonInfo, List<AyGroupBase> ayGroupBaseList) {
        List<AyPersonGroup> ayPersonGroupList = new ArrayList<>();
        ayGroupBaseList.forEach(item -> {
            AyPersonGroup ayPersonGroup = new AyPersonGroup();
            ayPersonGroup.setPersonId(ayPersonInfo.getId());
            ayPersonGroup.setGroupId(item.getId());
            ayPersonGroupList.add(ayPersonGroup);
        });
        return ayPersonGroupService.saveBatch(ayPersonGroupList);
    }

    @Override
    public void sendAddToKafka(List<String> plateformGroupIds, String base64Image, Integer personId) {
        FacePersonReqDto facePersonReqDto = new FacePersonReqDto();
        facePersonReqDto.setGroupIds(new ArrayList<>(plateformGroupIds));
        facePersonReqDto.setFaceId(personId);
        facePersonReqDto.setImage(base64Image);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("OpType", "insertPerson");
        jsonObject.put("Data", JSONObject.toJSONString(facePersonReqDto));
        MqttOp.publish(MediaMqConstants.PERSON_TOPIC, jsonObject.toJSONString());
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public JSONObject updateAyPersonInfo(AyPersonInfo ayPersonInfo) {
        JSONObject jsonObject = new JSONObject();
        String base64Image = Base64Utils.picToBase64(ayPersonInfo.getImageUrl());
        //人像基础信息修改
        boolean flag = this.updateById(ayPersonInfo);
        //人像和库关系修改
        if (flag) {
            flag = updateAyPersonGroup(ayPersonInfo);
        }
        //人像扩展信息修改
        if (flag) {
            flag = updateAyPersonFace(ayPersonInfo, base64Image);
        }
        if (flag) {
            ArrayList<Integer> arrayList = new ArrayList<>();
            arrayList.add(ayPersonInfo.getId());
            //删除比特数据
            List<AyPersonGroup> personGroups = ayPersonGroupService.selectGroupAndFace(arrayList);

            //新增比特数据
            LambdaQueryWrapper<AyGroupBase> wrapper = new QueryWrapper<AyGroupBase>().lambda();
            wrapper.in(ayPersonInfo.getGroupIds().size() > 0, AyGroupBase::getId, ayPersonInfo.getGroupIds());
            List<AyGroupBase> ayGroupBaseList = ayGroupBaseService.list(wrapper);

            List<String> plateformGroupIds = ayGroupBaseList
                    .stream()
                    .map(AyGroupBase::getPlateformGroupId)
                    .collect(Collectors.toList());
            jsonObject.put("personGroups", JSONObject.toJSONString(personGroups));
            jsonObject.put("base64Image", base64Image);
            jsonObject.put("plateformGroupIds", JSONObject.toJSONString(plateformGroupIds));
            return jsonObject;
        }
        return null;
    }

    private boolean updateAyPersonFace(AyPersonInfo ayPersonInfo, String base64Image) {
        AyPersonFace ayPersonFace = ayPersonFaceService.selectByPersonId(ayPersonInfo.getId());
        ayPersonFace.setPersonId(ayPersonInfo.getId());
        ayPersonFace.setImageId(ayPersonInfo.getImageId());
        ayPersonFace.setImageUrl(ayPersonInfo.getImageUrl());
        ayPersonFace.setFeature(remoteGetFeature(base64Image));
        return ayPersonFaceService.updateById(ayPersonFace);
    }

    private boolean updateAyPersonGroup(AyPersonInfo ayPersonInfo) {
        boolean flag = true;
        //库和人像关系修改
        //1. 查询人关联的库数据
        List<Integer> oldGroupIds = ayPersonGroupService.selectByPersonId(ayPersonInfo.getId());
        List<Integer> groupIds = ayPersonInfo.getGroupIds();
        //去除不存在
        oldGroupIds.removeAll(groupIds);
        if (!oldGroupIds.isEmpty()) {
            //删除不需要的关联的信息
            flag = ayPersonGroupService.removeByPersonIdAndGroupId(ayPersonInfo.getId(), oldGroupIds);
        }

        if (!groupIds.isEmpty() && flag) {
            List<AyPersonGroup> ayPersonGroupList = new ArrayList<>();
            groupIds.forEach(item -> {
                AyPersonGroup personGroup = ayPersonGroupService.selectByPersonAndGroup(ayPersonInfo.getId(), item);
                if (personGroup == null) {
                    AyPersonGroup ayPersonGroup = new AyPersonGroup();
                    ayPersonGroup.setPersonId(ayPersonInfo.getId());
                    ayPersonGroup.setGroupId(item);
                    ayPersonGroupList.add(ayPersonGroup);
                }
            });

            if (!ayPersonGroupList.isEmpty()) {
                flag = ayPersonGroupService.saveBatch(ayPersonGroupList);
            }
        }
        return flag;
    }

    /**
     * 远程获取特征值
     */
    private String remoteGetFeature(String base64Image) {
        FeatureExtractionReqDto featureExtractionReqDto = new FeatureExtractionReqDto();
        featureExtractionReqDto.setImageData(base64Image);
        ResponseResult result = faceService.featureExtraction(featureExtractionReqDto);
        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(result.getData()));
        return jsonObject.getString("feature");
    }

    /**
     * 查询详情
     *
     * @param id 人脸id
     * @return AyPersonInfoVo
     */
    @Override
    public AyPersonInfoVo selectById(Integer id) {
        //查询信息
        AyPersonInfo ayPersonInfo = this.getById(id);
        //获取字典值
        Map<Integer, String> idCard = selectByValue("idCard");
        //获取字典值
        Map<Integer, String> education = selectByValue("education");
        //设置字典值
        ayPersonInfo.setIdTypeString(idCard.get(ayPersonInfo.getIdType()));
        //设置字典值
        ayPersonInfo.setEducationString(education.get(ayPersonInfo.getEducation()));
        ayPersonInfo.setGroupIds(ayPersonGroupService.selectByPersonId(ayPersonInfo.getId()));
        //查询关联图片信息
        AyPersonFace ayPersonFace = ayPersonFaceService.selectByPersonId(id);
        ayPersonInfo.setImageUrl(ayPersonFace.getImageUrl());
        return BeanCopyUtils.copy(ayPersonInfo, AyPersonInfoVo.class);
    }

    /**
     * 删除
     *
     * @param personIds 人脸id集合
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<AyPersonGroup> removeAyPersonInfo(List<Integer> personIds) {
        //查询出三方ID(需要删除的比特大陆的人脸ID)删除人脸和库关联关系
        List<AyPersonGroup> personGroups = ayPersonGroupService.selectGroupAndFace(personIds);
        LambdaQueryWrapper<AyPersonGroup> personGroupWrapper = new QueryWrapper<AyPersonGroup>().lambda();
        personGroupWrapper
                .in(personIds.size() > 0, AyPersonGroup::getPersonId, personIds);
        ayPersonGroupService.remove(personGroupWrapper);

        //删除人脸信息
        LambdaQueryWrapper<AyPersonInfo> wrapper = new QueryWrapper<AyPersonInfo>().lambda();
        wrapper.eq(AyPersonInfo::getStatus, 1).in(AyPersonInfo::getId, personIds);
        boolean flag = this.update(new AyPersonInfo(0), wrapper);
        return flag ? personGroups : new ArrayList<>();
    }

    @Override
    public Map<Integer, String> selectByValue(String value) {
        List<SysDictVo> sysDictVos = this.baseMapper.selectByValue(value);
        if (!sysDictVos.isEmpty()) {
            return sysDictVos.stream().collect(Collectors.toMap(SysDictVo::getCode, SysDictVo::getLabel));
        }
        return null;
    }

    @Override
    public boolean batchImport(MultipartFile multipartFile, List<Integer> ids) {
        //默认位置
        String path = "D:\\Projects\\IdeaProjects\\relytosoft-mizar-analyze-dev\\";
        //文件名称
        long millis = System.currentTimeMillis();
        //解压文件夹
        File outfile = new File(path + millis);
        try {
            ZipUtil.unzip(multipartFile.getInputStream(), outfile, Charset.forName("GBK"));
            File[] files = outfile.listFiles();
            if (files != null && files.length > 0) {
                for (File file : files) {
                    AyPersonInfo ayPersonInfo = new AyPersonInfo();
                    //读取图片转换为流
                    FileInputStream fis = new FileInputStream(file);
                    byte[] data = inputStreamToBytes(fis);
//                    SysFile sysFile = remoteFileService.upload(null, file.getName(), null, data, UpLoadFileEnum.GENERAL.getCode());
                    SysFile sysFile = remoteFileService.upload(null, file.getName(), null, data, 1);
                    ayPersonInfo.setImageUrl(sysFile.getUrl());
                    //获取文件名
                    String name = file.getName();
                    //最后一个点开始切割
                    int i = name.lastIndexOf(".");
                    //截取字符串
                    String substring = name.substring(0, name.length() - i);
                    //封装数据
                    String[] split = substring.split("\\+");
                    ayPersonInfo.setName(split[0]);
                    ayPersonInfo.setSex("男".equals(split[1]) ? 0 : 1);
                    ayPersonInfo.setGroupIds(ids);
                    addAyPersonInfo(ayPersonInfo);
                    fis.close();
                    Files.delete(Paths.get(file.getPath()));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        outfile.delete();
        return true;
    }

    @Override
    public void sendRemoveToKafka(List<AyPersonGroup> personGroups) {
        log.info("需要删除的BTDL数据=={}", personGroups.toString());
        personGroups.forEach(item -> {
            if (item.getPlatformPersonId() == null) {
                FacePersonReqDto facePersonReqDto = new FacePersonReqDto();
                facePersonReqDto.setGroupId(item.getPlateformGroupId());
                facePersonReqDto.setPersonId(item.getPlatformPersonId());
                //发送消息进行数据处理
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("OpType", "deletePerson");
                jsonObject.put("Data", JSONObject.toJSONString(facePersonReqDto));
                MqttOp.publish(MediaMqConstants.PERSON_TOPIC, jsonObject.toJSONString());
            }
        });
    }

    public byte[] inputStreamToBytes(InputStream ins) {
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            int i = -1;
            byte[] buf = new byte[BUFFER_SIZE];
            while ((i = ins.read(buf)) != -1) {
                baos.write(buf, 0, i);
            }
            data = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }
}